//
// GPSMapEdit
// (c) Konstantin Galichsky (kg@geopainting.com), 2007
//
// Draw house numbers along roads.
//

# include "StdAfx.h"
# include "DrawNumbers.h"
# include "Map.h"
# include "Types.h"
# include "Selection.h"
# include "fast_floor.h"
# include "Globals.h"

static
void _DrawNumber (HDC _hDC, int _x, int _y, WORD _wNumber) {
	char strNumber [10];
	::sprintf (strNumber, "%d", _wNumber);

	::TextOut (_hDC, _x, _y, strNumber, ::strlen (strNumber));
}

static
bool _IsNumberEnabled (WORD _wN, WORD _wParity) {
	switch (_wParity) {
		default:
		case 0: return false;
		case 1: return (_wN & 0x1) == 0;
		case 2: return (_wN & 0x1) != 0;
		case 3: return true;
	}
}

const int c_a = 15;
const int c_b = 6;

void DrawNumbers (HDC _hDC, const rgn_t & _rgn, const rgn_element_t & _element, const POINT * _pPoints, size_t _cPoints) {
	if ((g_map.pTypeSet->dwFeatures & CTypeSet::tsfHasRoadAddress) == 0)
		return;

	if (! _element.pExtra)
		return;

	::SelectObject (_hDC, g_hFont);
	::SetTextColor (_hDC, c_colBlue);
	::SetTextAlign (_hDC, TA_CENTER | TA_BASELINE);

	const points_t & points = _element.points;
	const point_extras_t & PointExtras = _element.pExtra->PointExtras;
	const size_t cPoints = __min (__min (PointExtras.size (), points.size ()), _cPoints);

	const numbers_t * pNumbersPrev = NULL;
	for (size_t c = 0; c < cPoints; ++ c) {
		const numbers_t & Numbers = PointExtras [c].Numbers;
		if (! Numbers.bHasNumbers)
			continue;

		if (c + 1 < cPoints) {
			float fLen = 0;
			size_t c2;
			for (c2 = c; c2 + 1 < cPoints; ++ c2) {
				if (c2 != c && PointExtras [c2].Numbers.bHasNumbers)
					break;

				const POINT & pt0 = _pPoints [c2];
				const POINT & pt1 = _pPoints [c2 + 1];
				fLen += ::hypot (float (pt1.x - pt0.x), float (pt1.y - pt0.y));
			}
			if (fLen >= 3*c_a) {
				fLen = 0;
				for (size_t c2 = c; c2 + 1 < cPoints; ++ c2) {
					if (c2 != c && PointExtras [c2].Numbers.bHasNumbers)
						break;

					const POINT & pt0 = _pPoints [c2];
					const POINT & pt1 = _pPoints [c2 + 1];
					const int dx = pt1.x - pt0.x;
					const int dy = pt1.y - pt0.y;
					const float l = ::hypot (float (dx), float (dy));
					if (fLen + l > c_a) {
						const int nXLeft  = fast_floor (pt0.x + dx*(c_a - fLen)/l + dy*c_b/l     + .5f);
						const int nYLeft  = fast_floor (pt0.y + dy*(c_a - fLen)/l - dx*c_b/l + 4 + .5f);
						const int nXRight = fast_floor (pt0.x + dx*(c_a - fLen)/l - dy*c_b/l     + .5f);
						const int nYRight = fast_floor (pt0.y + dy*(c_a - fLen)/l + dx*c_b/l + 4 + .5f);

						if (Numbers.wLeftParity != 0)
							_DrawNumber (_hDC, nXLeft,  nYLeft,  Numbers.wLeftAfter);
						if (Numbers.wRightParity != 0)
							_DrawNumber (_hDC, nXRight, nYRight, Numbers.wRightAfter);

						break;
					}
					fLen += l;
				}
			}
		}

		if (c > 0) {
			float fLen = 0;
			int n2;
			for (n2 = c; n2 - 1 >= 0; -- n2) {
				if (n2 != (int) c && PointExtras [n2].Numbers.bHasNumbers)
					break;

				const POINT & pt0 = _pPoints [n2];
				const POINT & pt1 = _pPoints [n2 - 1];
				fLen += ::hypot (float (pt1.x - pt0.x), float (pt1.y - pt0.y));
			}
			if (fLen >= 3*c_a) {
				fLen = 0;
				for (n2 = c; n2 - 1 >= 0; -- n2) {
					if (n2 != (int) c && PointExtras [n2].Numbers.bHasNumbers)
						break;

					const POINT & pt0 = _pPoints [n2];
					const POINT & pt1 = _pPoints [n2 - 1];
					const int dx = pt1.x - pt0.x;
					const int dy = pt1.y - pt0.y;
					const float l = ::hypot (float (dx), float (dy));
					if (fLen + l > c_a) {
						const int nXLeft  = fast_floor (pt0.x + dx*(c_a - fLen)/l - dy*c_b/l     + .5f);
						const int nYLeft  = fast_floor (pt0.y + dy*(c_a - fLen)/l + dx*c_b/l + 4 + .5f);
						const int nXRight = fast_floor (pt0.x + dx*(c_a - fLen)/l + dy*c_b/l     + .5f);
						const int nYRight = fast_floor (pt0.y + dy*(c_a - fLen)/l - dx*c_b/l + 4 + .5f);

						if (pNumbersPrev && pNumbersPrev->wLeftParity  != 0)
							_DrawNumber (_hDC, nXLeft,  nYLeft,  Numbers.wLeftBefore);
						if (pNumbersPrev && pNumbersPrev->wRightParity != 0)
							_DrawNumber (_hDC, nXRight, nYRight, Numbers.wRightBefore);

						break;
					}
					fLen += l;
				}
			}
		}

		pNumbersPrev = & Numbers;
	}
}

void DrawNumbersSelected (HDC _hDC, const rgn_t & _rgn, const rgn_element_t & _element, const POINT * _pPoints, size_t _cPoints) {
	if ((g_map.pTypeSet->dwFeatures & CTypeSet::tsfHasRoadAddress) == 0)
		return;

	if (! _element.pExtra)
		return;

	if (! g_Selection.empty () && g_Selection [0].pLayer && & * g_Selection [0].iRgn == & _rgn) {
		//
		// Show intermediate numbers for selected road.
		//
		::SelectObject (_hDC, g_hFont);
		::SetTextColor (_hDC, c_colRed);
		::SetBkMode    (_hDC, TRANSPARENT);
		::SetTextAlign (_hDC, TA_CENTER | TA_BASELINE);

		const points_t & points = _element.points;
		const point_extras_t & PointExtras = _element.pExtra->PointExtras;
		const size_t cPoints = __min (__min (PointExtras.size (), points.size ()), _cPoints);

		size_t cPointPrev = 0;
		const numbers_t * pNumbersPrev = NULL;
		for (size_t c = 0; c < cPoints; ++ c) {
			const numbers_t & Numbers = PointExtras [c].Numbers;
			if (! Numbers.bHasNumbers)
				continue;

			if (pNumbersPrev) {
				// Get net length.
				float fLen = 0;
				for (size_t c1 = cPointPrev; c1 < c; ++ c1) {
					const POINT & pt0 = _pPoints [c1];
					const POINT & pt1 = _pPoints [c1 + 1];
					const int dx = pt1.x - pt0.x;
					const int dy = pt1.y - pt0.y;
					const double l = ::hypot (float (dx), float (dy));
					fLen += l;
				}

				if (fLen >= c_a*(2 + ::labs (pNumbersPrev->wLeftAfter - Numbers.wLeftBefore)/2)) {
					const double fStep = (fLen - 2*c_a)/::labs (Numbers.wLeftBefore - pNumbersPrev->wLeftAfter);
					const int nNStep = Numbers.wLeftBefore > pNumbersPrev->wLeftAfter ? 1 : -1;
					WORD wN = pNumbersPrev->wLeftAfter + nNStep;
					
					double fLenRest = -c_a;
					for (size_t c1 = cPointPrev; c1 < c; ++ c1) {
						const POINT & pt0 = _pPoints [c1];
						const POINT & pt1 = _pPoints [c1 + 1];
						const int dx = pt1.x - pt0.x;
						const int dy = pt1.y - pt0.y;
						const double l = ::hypot (float (dx), float (dy));

						const size_t cN = __max (::fast_floor ((l + fLenRest)/fStep), 0);
						for (size_t c2 = 1; c2 <= cN; ++ c2) {
							if (wN == Numbers.wLeftBefore || wN == pNumbersPrev->wLeftAfter)
								continue;

							if (_IsNumberEnabled (wN, pNumbersPrev->wLeftParity)) {
								const double t = c2*fStep - fLenRest;
								_DrawNumber (
									_hDC,
									pt0.x + t*dx/l + c_b*dy/l,
									pt0.y + t*dy/l - c_b*dx/l + 4,
									wN
								);
							}
							wN += nNStep;
						}

						if (cN != 0)
							fLenRest = __max (l - cN*fStep + fLenRest, 0);
						else
							fLenRest += l;
					}
				}

				if (fLen >= c_a*(2 + ::labs (pNumbersPrev->wRightAfter - Numbers.wRightBefore)/2)) {
					const double fStep = (fLen - 2*c_a)/::labs (Numbers.wRightBefore - pNumbersPrev->wRightAfter);
					const int nNStep = Numbers.wRightBefore > pNumbersPrev->wRightAfter ? 1 : -1;
					WORD wN = pNumbersPrev->wRightAfter + nNStep;

					double fLenRest = -c_a;
					for (size_t c1 = cPointPrev; c1 < c; ++ c1) {
						const POINT & pt0 = _pPoints [c1];
						const POINT & pt1 = _pPoints [c1 + 1];
						const int dx = pt1.x - pt0.x;
						const int dy = pt1.y - pt0.y;
						const double l = ::hypot (float (dx), float (dy));

						const size_t cN = __max (::fast_floor ((l + fLenRest)/fStep), 0);
						for (size_t c2 = 1; c2 <= cN; ++ c2) {
							if (wN == Numbers.wRightBefore || wN == pNumbersPrev->wRightAfter)
								continue;

							if (_IsNumberEnabled (wN, pNumbersPrev->wRightParity)) {
								const double t = c2*fStep - fLenRest;
								_DrawNumber (
									_hDC,
									pt0.x + t*dx/l - c_b*dy/l,
									pt0.y + t*dy/l + c_b*dx/l + 4,
									wN
								);
							}
							wN += nNStep;
						}

						if (cN != 0)
							fLenRest = __max (l - cN*fStep + fLenRest, 0);
						else
							fLenRest += l;
					}
				}
			}

			pNumbersPrev = & Numbers;
			cPointPrev = c;
		}
	}
}
